home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / kernel / proc / procTable.c < prev    next >
C/C++ Source or Header  |  1992-12-18  |  17KB  |  674 lines

  1. /* 
  2.  * procTable.c --
  3.  *
  4.  *    Routines to manage the process table.  This maintains a monitor
  5.  *    that synchronizes access to PCB's.
  6.  *
  7.  * Copyright 1985, 1988 Regents of the University of California
  8.  * Permission to use, copy, modify, and distribute this
  9.  * software and its documentation for any purpose and without
  10.  * fee is hereby granted, provided that the above copyright
  11.  * notice appear in all copies.  The University of California
  12.  * makes no representations about the suitability of this
  13.  * software for any purpose.  It is provided "as is" without
  14.  * express or implied warranty.
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/proc/procTable.c,v 9.16 92/01/06 15:03:44 kupfer Exp $ SPRITE (Berkeley)";
  19. #endif not lint
  20.  
  21. #include <sprite.h>
  22. #include <mach.h>
  23. #include <proc.h>
  24. #include <procInt.h>
  25. #include <sync.h>
  26. #include <sched.h>
  27. #include <timer.h>
  28. #include <list.h>
  29. #include <vm.h>
  30. #include <sys.h>
  31. #include <stdlib.h>
  32. #include <rpc.h>
  33.  
  34. static Sync_Lock    tableLock;
  35. #define    LOCKPTR &tableLock
  36.  
  37. static Proc_ControlBlock  *RunningProcesses[MACH_MAX_NUM_PROCESSORS];
  38. Proc_ControlBlock  **proc_RunningProcesses = RunningProcesses;
  39. Proc_ControlBlock **proc_PCBTable;
  40.  
  41. #define PROC_MAX_PROCESSES 256
  42. #define PROC_PCB_NUM_ALLOC 16
  43. int proc_MaxNumProcesses;
  44.  
  45. int procLastSlot = 0;    /* Circular index into proctable for choosing slots */
  46. static int realMaxProcesses;    /* The absolute number of process table
  47.                  * entries, not necessarily allocated yet. */
  48. static int entriesInUse = 0;    /* Number of PCB's in use. */
  49.  
  50. static void     InitPCB _ARGS_((Proc_ControlBlock *pcbPtr, int i));
  51. static void    AddPCBs _ARGS_((Proc_ControlBlock **procPtrPtr));
  52.  
  53.  
  54. /*
  55.  * ----------------------------------------------------------------------------
  56.  *
  57.  * ProcInitTable --
  58.  *
  59.  *    Initializes the PCB table and running process table.  Must be called
  60.  *    at initialization time with interrupts off.  Initializes an array
  61.  *    of PROC_MAX_PROCESSES pointers to PCB's but only allocates
  62.  *    PROC_PCB_NUM_ALLOC entries at first.  The rest are done dynamically.
  63.  *
  64.  * Results:
  65.  *    None.
  66.  *
  67.  * Side effects:
  68.  *    The PCB table is initialized.
  69.  *
  70.  * ----------------------------------------------------------------------------
  71.  */
  72.  
  73. void
  74. ProcInitTable()
  75. {
  76.     register    int           i;
  77.     register    Proc_ControlBlock *pcbPtr;
  78.     int     maxRunningProcesses;
  79.  
  80.     maxRunningProcesses = MACH_MAX_NUM_PROCESSORS;
  81.     proc_MaxNumProcesses     = PROC_PCB_NUM_ALLOC;
  82.     realMaxProcesses         = PROC_MAX_PROCESSES;
  83.  
  84.     proc_PCBTable = (Proc_ControlBlock **)
  85.         Vm_BootAlloc(realMaxProcesses * sizeof(pcbPtr));
  86.  
  87.     for (i = 0; i < proc_MaxNumProcesses; i++) {
  88.     pcbPtr = (Proc_ControlBlock *) Vm_BootAlloc(sizeof(Proc_ControlBlock));
  89.     proc_PCBTable[i] = pcbPtr;
  90.     InitPCB(pcbPtr, i);
  91.     }
  92.  
  93.     /*
  94.      * Set the rest of the proc table to catch any misuse of nonexistent
  95.      * entries.
  96.      */
  97.  
  98.     for (i = proc_MaxNumProcesses; i < realMaxProcesses; i++) {
  99.     proc_PCBTable[i] = (Proc_ControlBlock *) NIL;
  100.     }
  101.  
  102.     for (i = 0; i < maxRunningProcesses; i++) {
  103.         proc_RunningProcesses[i] = (Proc_ControlBlock *) NIL;
  104.     }
  105.     Sync_LockInitDynamic(&tableLock, "Proc:tableLock");
  106. }
  107.  
  108.  
  109. /*
  110.  * ----------------------------------------------------------------------------
  111.  *
  112.  * InitPCB --
  113.  *
  114.  *    Initializes a process control block.
  115.  *
  116.  * Results:
  117.  *    None.
  118.  *
  119.  * Side effects:
  120.  *    None.
  121.  *
  122.  * ----------------------------------------------------------------------------
  123.  */
  124. static void
  125. InitPCB(pcbPtr, i)
  126.     Proc_ControlBlock *pcbPtr;
  127.     int i;
  128. {
  129.     List_InitElement((List_Links *)pcbPtr);
  130.     pcbPtr->state        = PROC_UNUSED;
  131.     pcbPtr->processID    = i;
  132.     pcbPtr->genFlags    = 0;
  133.  
  134.     /*
  135.      *  Initialize the pointers to the list headers and the
  136.      *  PCB entry. These values do not change when the PCB
  137.      *  entry is re-used.
  138.      */
  139.     pcbPtr->childList    = &(pcbPtr->childListHdr);
  140.     pcbPtr->siblingElement.procPtr    = pcbPtr;
  141.     pcbPtr->familyElement.procPtr    = pcbPtr;
  142.  
  143.     /*
  144.      *  Set the links to NIL to catch any invalid uses of
  145.      *  the lists before they are properly initialized.
  146.      *  These pointers change whenever the PCB entry is re-used.
  147.      */
  148.     pcbPtr->childListHdr.nextPtr    = (List_Links *) NIL;
  149.     pcbPtr->childListHdr.prevPtr    = (List_Links *) NIL;
  150.  
  151.     List_InitElement((List_Links *)&pcbPtr->siblingElement);
  152.     List_InitElement((List_Links *)&pcbPtr->familyElement);
  153.  
  154.     pcbPtr->eventHashChain.procPtr = pcbPtr;
  155.     List_InitElement((List_Links *)&pcbPtr->eventHashChain);
  156.     pcbPtr->event = NIL;
  157.  
  158.     pcbPtr->peerHostID = NIL;
  159.     pcbPtr->peerProcessID = (Proc_PID) NIL;
  160.     pcbPtr->remoteExecBuffer = (Address) NIL;
  161.     pcbPtr->migCmdBuffer = (Address) NIL;
  162.     pcbPtr->migCmdBufSize = 0;
  163.     pcbPtr->migFlags = 0;
  164.     pcbPtr->argString = (char *) NIL;
  165. #ifdef LOCKDEP
  166.     pcbPtr->lockStackSize = 0;
  167. #endif
  168.     pcbPtr->vmPtr = (Vm_ProcInfo *)NIL;
  169.     pcbPtr->fsPtr = (Fs_ProcessState *)NIL;
  170.     pcbPtr->rpcClientProcess = (Proc_ControlBlock *) NIL;
  171.  
  172.     pcbPtr->waitToken = 0;
  173.     pcbPtr->timerArray = (struct ProcIntTimerInfo *) NIL;
  174.  
  175.     pcbPtr->kcallTable = mach_NormalHandlers;
  176.     pcbPtr->specialHandling = 0;
  177.     pcbPtr->machStatePtr = (struct Mach_State *)NIL;
  178. #ifndef CLEAN_LOCK
  179.     Sync_SemInitDynamic(&pcbPtr->lockInfo, "Proc:perPCBlock");
  180. #endif
  181. #ifdef LOCKREG
  182.     Sync_LockRegister(&pcbPtr->lockInfo);
  183. #endif
  184. }
  185.  
  186.  
  187. /*
  188.  * ----------------------------------------------------------------------------
  189.  *
  190.  *  AddPCBs --
  191.  *
  192.  *    Add new proc_ControlBlocks with sched_Mutex locked.  This avoids
  193.  *    conflicts accessing the proc_MaxNumProcesses variable, such as in
  194.  *    the Sched_ForgetUsage routine.
  195.  *
  196.  * Results:
  197.  *    None.
  198.  *
  199.  * Side effects:
  200.  *    The global array of process control blocks is updated to point
  201.  *    to the PCB's pointed to by procPtrPtr, and the count of useable entries
  202.  *    is updated.
  203.  *
  204.  * ----------------------------------------------------------------------------
  205.  */
  206.  
  207. static void
  208. AddPCBs(procPtrPtr)
  209.     Proc_ControlBlock **procPtrPtr;
  210. {
  211.     register int i;
  212.     
  213.     /*
  214.      *  Gain exclusive access to the process table.
  215.      */
  216.     MASTER_LOCK(sched_MutexPtr);
  217.  
  218.     for (i = 0; i < PROC_PCB_NUM_ALLOC; i++) {
  219.     proc_PCBTable[proc_MaxNumProcesses] = *procPtrPtr;
  220.     procPtrPtr++;
  221.     proc_MaxNumProcesses++;
  222.     }
  223.  
  224.     MASTER_UNLOCK(sched_MutexPtr);
  225. }
  226.     
  227.  
  228. /*
  229.  * ----------------------------------------------------------------------------
  230.  *
  231.  * Proc_InitMainProc --
  232.  *
  233.  *    Finish initializing the process table by making a proc table entry
  234.  *    for the main process.  Called with interrupts disabled.
  235.  *
  236.  * Results:
  237.  *    None.
  238.  *
  239.  * Side effects:
  240.  *    The first element of the proc table is modified, and the count of
  241.  *    used entries is set to 1.
  242.  *
  243.  * ----------------------------------------------------------------------------
  244.  */
  245.  
  246. void
  247. Proc_InitMainProc()
  248. {
  249.     register    Proc_ControlBlock *procPtr;
  250.  
  251. #define MAIN_PID 0
  252.  
  253.     entriesInUse = 1;
  254.     
  255.     procPtr = proc_PCBTable[MAIN_PID];
  256.  
  257.     /*
  258.      *  Initialize the main process.
  259.      */
  260.     procPtr->state        = PROC_RUNNING;
  261.     procPtr->genFlags        = PROC_KERNEL;
  262.     procPtr->syncFlags        = 0;
  263.     procPtr->schedFlags        = 0;
  264.     procPtr->processID         = MAIN_PID | (1 << PROC_GEN_NUM_SHIFT) | 
  265.                     (rpc_SpriteID << PROC_ID_NUM_SHIFT);
  266.     procPtr->parentID        = procPtr->processID;
  267.     procPtr->billingRate     = PROC_NORMAL_PRIORITY;
  268.     procPtr->recentUsage     = 0;
  269.     procPtr->weightedUsage     = 0;
  270.     procPtr->unweightedUsage     = 0;
  271.     procPtr->kernelCpuUsage.ticks     = timer_TicksZeroSeconds;
  272.     procPtr->userCpuUsage.ticks       = timer_TicksZeroSeconds;
  273.     procPtr->childKernelCpuUsage.ticks = timer_TicksZeroSeconds;
  274.     procPtr->childUserCpuUsage.ticks  = timer_TicksZeroSeconds;
  275.     procPtr->numQuantumEnds     = 0;
  276.     procPtr->numWaitEvents     = 0;
  277.  
  278.     procPtr->Prof_Buffer        = (short *) NIL;
  279.     procPtr->Prof_BufferSize    = 0;
  280.     procPtr->Prof_Offset        = 0;
  281.     procPtr->Prof_Scale         = 0;
  282.     procPtr->Prof_PC            = 0;
  283.  
  284.     Mach_InitFirstProc(procPtr);
  285.     Vm_ProcInit(procPtr);
  286.     (void) VmMach_SetupContext(procPtr);
  287.  
  288.     procPtr->familyID         = PROC_NO_FAMILY;    /* not in a family */
  289.     
  290.     List_Init(procPtr->childList);
  291.  
  292.     procPtr->userID        = 0;
  293.     procPtr->effectiveUserID    = 0;
  294.  
  295.     Sig_ProcInit(procPtr);
  296.  
  297.     procPtr->processor = Mach_GetProcessorNumber();
  298.     Proc_SetCurrentProc(procPtr);
  299.  
  300.     ProcInitMainEnviron(procPtr);
  301.  
  302.     ProcFamilyHashInit();
  303.  
  304.     procPtr->peerProcessID = (Proc_PID) NIL;
  305.     procPtr->peerHostID = (int) NIL;
  306.     procPtr->remoteExecBuffer = (Address) NIL;
  307. }
  308.  
  309.  
  310. /*
  311.  * ----------------------------------------------------------------------------
  312.  *
  313.  * Proc_LockPID --
  314.  *
  315.  *    Determine the validity of the given pid and if valid return a pointer
  316.  *    to the proc table entry.  The proc table entry is returned locked.
  317.  *
  318.  * Results:
  319.  *    Pointer to proc table entry.
  320.  *
  321.  * Side effects:
  322.  *    Proc table entry is locked.
  323.  *
  324.  * ----------------------------------------------------------------------------
  325.  */
  326.  
  327. ENTRY Proc_ControlBlock *
  328. Proc_LockPID(pid)
  329.     Proc_PID    pid;
  330. {
  331.     register    Proc_ControlBlock *procPtr;
  332. #ifndef CLEAN_LOCK
  333.     register    Sync_Semaphore      *lockPtr;
  334. #endif
  335.  
  336.     LOCK_MONITOR;
  337.  
  338.     if (Proc_PIDToIndex(pid) >= proc_MaxNumProcesses) {
  339.     UNLOCK_MONITOR;
  340.     return((Proc_ControlBlock *) NIL);
  341.     }
  342.     procPtr = proc_PCBTable[Proc_PIDToIndex(pid)];
  343. #ifndef CLEAN_LOCK
  344.     lockPtr = &(procPtr->lockInfo);
  345. #endif
  346.  
  347.     while (TRUE) {
  348.     if (procPtr->state == PROC_UNUSED || procPtr->state == PROC_DEAD) {
  349.         procPtr = (Proc_ControlBlock *) NIL;
  350.         break;
  351.     }
  352.  
  353.     if (procPtr->genFlags & PROC_LOCKED) {
  354.         do {
  355.         Sync_RecordMiss(lockPtr);
  356.         (void) Sync_Wait(&procPtr->lockedCondition, FALSE);
  357.         } while (procPtr->genFlags & PROC_LOCKED);
  358.     } else {
  359.         if (!Proc_ComparePIDs(procPtr->processID, pid)) {
  360.         procPtr = (Proc_ControlBlock *) NIL;
  361.         } else {
  362.         procPtr->genFlags |= PROC_LOCKED;
  363.         Sync_RecordHit(lockPtr);
  364.         Sync_StoreDbgInfo(lockPtr, FALSE);
  365.         Sync_AddPrior(lockPtr);
  366.         }
  367.         break;
  368.     }
  369.     }
  370.  
  371.     UNLOCK_MONITOR;
  372.     return(procPtr);
  373. }
  374.  
  375.  
  376. /*
  377.  * ----------------------------------------------------------------------------
  378.  *
  379.  * Proc_Lock --
  380.  *
  381.  *    Lock the proc table entry.
  382.  *
  383.  * Results:
  384.  *    None.
  385.  *
  386.  * Side effects:
  387.  *    Proc table entry is locked.
  388.  *
  389.  * ----------------------------------------------------------------------------
  390.  */
  391.  
  392. ENTRY void
  393. Proc_Lock(procPtr)
  394.     register    Proc_ControlBlock *procPtr;
  395. {
  396. #ifndef CLEAN_LOCK
  397.     register    Sync_Semaphore      *lockPtr;
  398. #endif
  399.  
  400.     LOCK_MONITOR;
  401.  
  402. #ifndef CLEAN_LOCK
  403.     lockPtr = &(procPtr->lockInfo);
  404. #endif
  405.  
  406.     while (procPtr->genFlags & PROC_LOCKED) {
  407.     Sync_RecordMiss(lockPtr);
  408.     (void) Sync_Wait(&procPtr->lockedCondition, FALSE);
  409.     }
  410.     procPtr->genFlags |= PROC_LOCKED;
  411.  
  412.     Sync_RecordHit(lockPtr);
  413.     Sync_StoreDbgInfo(lockPtr, FALSE);
  414.     Sync_AddPrior(lockPtr);
  415.  
  416.     UNLOCK_MONITOR;
  417. }
  418.  
  419.  
  420. /*
  421.  * ----------------------------------------------------------------------------
  422.  *
  423.  * Proc_Unlock --
  424.  *
  425.  *    Unlock the proc table entry.
  426.  *
  427.  * Results:
  428.  *    None.
  429.  *
  430.  * Side effects:
  431.  *    Proc table entry is unlocked.
  432.  *
  433.  * ----------------------------------------------------------------------------
  434.  */
  435.  
  436. ENTRY void
  437. Proc_Unlock(procPtr)
  438.     register    Proc_ControlBlock *procPtr;
  439. {
  440.     LOCK_MONITOR;
  441.  
  442.     if (!(procPtr->genFlags & PROC_LOCKED)) {
  443.     panic("Proc_Unlock: PCB not locked.\n");
  444.     }
  445.     procPtr->genFlags &= ~PROC_LOCKED;
  446.     Sync_Broadcast(&procPtr->lockedCondition);
  447.  
  448.     UNLOCK_MONITOR;
  449. }
  450.  
  451.  
  452. /*
  453.  *----------------------------------------------------------------------
  454.  *
  455.  * Proc_UnlockAndSwitch --
  456.  *
  457.  *    Unlock a PCB and perform a context switch to the given state.  
  458.  *    This is done atomically: no other process can lock the PCB before 
  459.  *    this process context switches.  
  460.  *
  461.  * Results:
  462.  *    None.
  463.  *
  464.  * Side effects:
  465.  *    None.
  466.  *
  467.  *----------------------------------------------------------------------
  468.  */
  469.  
  470. void
  471. Proc_UnlockAndSwitch(procPtr, state)
  472.     Proc_ControlBlock *procPtr;    /* the PCB to unlock */
  473.     Proc_State state;        /* the state to context switch to */
  474. {
  475.     LOCK_MONITOR;
  476.  
  477.     if (!(procPtr->genFlags & PROC_LOCKED)) {
  478.     panic("Proc_Unlock: PCB not locked.\n");
  479.     }
  480.     procPtr->genFlags &= ~PROC_LOCKED;
  481.     Sync_Broadcast(&procPtr->lockedCondition);
  482.  
  483.     Sync_UnlockAndSwitch(LOCKPTR, state);
  484. }
  485.  
  486.  
  487. /*
  488.  * ----------------------------------------------------------------------------
  489.  *
  490.  * ProcGetUnusedPCB --
  491.  *
  492.  *    Return the first unused PCB.
  493.  *
  494.  * Results:
  495.  *    Pointer to PCB.
  496.  *
  497.  * Side effects:
  498.  *    Proc table entry is locked and marked as PROC_NEW.
  499.  *
  500.  * ----------------------------------------------------------------------------
  501.  */
  502.  
  503. ENTRY Proc_ControlBlock *
  504. ProcGetUnusedPCB()
  505. {
  506.     register    Proc_ControlBlock     **procPtrPtr;
  507.     register    Proc_ControlBlock     *procPtr;
  508.     Proc_ControlBlock             *pcbArray[PROC_PCB_NUM_ALLOC];
  509.     register    int             i;
  510.     int                    generation;
  511.  
  512.     LOCK_MONITOR;
  513.  
  514.  
  515.  
  516.     /* 
  517.      * See if we need to allocate more process table entries.
  518.      */
  519.     if (entriesInUse == proc_MaxNumProcesses) {
  520.     if (proc_MaxNumProcesses > realMaxProcesses - PROC_PCB_NUM_ALLOC) {
  521.         panic("ProcGetUnusedPCB: PCB table full!!\n");
  522.     }
  523.     for (i = 0; i < PROC_PCB_NUM_ALLOC; i++) {
  524.         pcbArray[i] = (Proc_ControlBlock *)
  525.             Vm_RawAlloc(sizeof(Proc_ControlBlock));
  526.         InitPCB(pcbArray[i], proc_MaxNumProcesses + i);
  527.     }
  528.     AddPCBs(pcbArray);
  529.     }
  530.  
  531.     
  532.     
  533.     /*
  534.      * Scan the proc table looking for an unused slot.  The search is
  535.      * circular, starting just after the last slot chosen.  This is done
  536.      * so that slots are not re-used often so the generation number of
  537.      * each slot can just be a few bits wide.
  538.      */
  539.     for (i = procLastSlot, procPtrPtr = &proc_PCBTable[procLastSlot]; ; ) {
  540.     if ((*procPtrPtr)->state == PROC_UNUSED) {
  541.         break;
  542.     }
  543.     i++;
  544.     procPtrPtr++;
  545.     if (i >= proc_MaxNumProcesses) {
  546.         i = 0;
  547.         procPtrPtr = &proc_PCBTable[0];
  548.     }
  549.     /*
  550.      * Shouldn't hit this, but check to avoid infinite loop.
  551.      */
  552.     if (i == procLastSlot) {
  553.         panic("ProcGetUnusedPCB: PCB table full!!\n");
  554.     }
  555.     }
  556.  
  557.     procLastSlot = i+1;
  558.     if (procLastSlot >= proc_MaxNumProcesses) {
  559.     procLastSlot = 0;
  560.     }
  561.     procPtr = *procPtrPtr;
  562.     procPtr->genFlags = PROC_LOCKED;
  563.     procPtr->migFlags = 0;
  564.     procPtr->state = PROC_NEW;
  565.     /*
  566.      *  The PCB entry has a generation number that is incremented each time
  567.      *  the entry is re-used. The low-order bits are in index into
  568.      *  the PCB table.
  569.      */
  570.     generation = (procPtr->processID & PROC_GEN_NUM_MASK) >> PROC_GEN_NUM_SHIFT;
  571.     generation += 1;
  572.     generation = (generation << PROC_GEN_NUM_SHIFT) & PROC_GEN_NUM_MASK;
  573.     procPtr->processID = i | generation | (rpc_SpriteID << PROC_ID_NUM_SHIFT);
  574.  
  575.     entriesInUse++;
  576.  
  577.     UNLOCK_MONITOR;
  578.  
  579.     return(procPtr);
  580. }
  581.  
  582.  
  583. /*
  584.  * ----------------------------------------------------------------------------
  585.  *
  586.  * ProcFreePCB --
  587.  *
  588.  *    Mark the given PCB as unused.
  589.  *
  590.  * Results:
  591.  *    None.
  592.  *
  593.  * Side effects:
  594.  *    Proc table entry marked as PROC_UNUSED.
  595.  *
  596.  * ----------------------------------------------------------------------------
  597.  */
  598.  
  599. ENTRY void
  600. ProcFreePCB(procPtr)
  601.     register    Proc_ControlBlock     *procPtr;
  602. {
  603. #ifdef LOCKREG
  604.     register    Sync_Semaphore      *lockPtr;
  605. #endif
  606.  
  607.     LOCK_MONITOR;
  608.  
  609. #ifdef LOCKREG
  610.     lockPtr = &(procPtr->lockInfo);
  611. #endif
  612.  
  613.     while (procPtr->genFlags & PROC_LOCKED) {
  614. #ifdef LOCKREG
  615.     Sync_RecordMiss(lockPtr);
  616. #endif
  617.     (void) Sync_Wait(&procPtr->lockedCondition, FALSE);
  618.     }
  619.     procPtr->state = PROC_UNUSED;
  620.     procPtr->genFlags = 0;
  621.     entriesInUse--;
  622.  
  623. #ifdef LOCKREG
  624.     Sync_RecordHit(lockPtr);
  625. #endif
  626.  
  627.     UNLOCK_MONITOR;
  628. }
  629.  
  630.  
  631. /*
  632.  * ----------------------------------------------------------------------------
  633.  *
  634.  * ProcTableMatch --
  635.  *
  636.  *    Go through the process table and return an array of process
  637.  *    IDs for which the specified function returns TRUE.
  638.  *
  639.  * Results:
  640.  *    The array of PIDs and the number of matches are returned.
  641.  *
  642.  * Side effects:
  643.  *    None.
  644.  *
  645.  * ----------------------------------------------------------------------------
  646.  */
  647.  
  648. ENTRY int
  649. ProcTableMatch(maxPids, booleanFuncPtr, pidArray)
  650.     int maxPids;            /* size of pidArray */
  651.     Boolean (*booleanFuncPtr) _ARGS_((Proc_ControlBlock *pcbPtr));
  652.                     /* function to match */
  653.     Proc_PID *pidArray;            /* array to store results */
  654. {
  655.     Proc_ControlBlock *pcbPtr;
  656.     int i;
  657.     int matched = 0;
  658.     
  659.     LOCK_MONITOR;
  660.  
  661.     for (i = 0; i < proc_MaxNumProcesses && matched < maxPids; i++) {
  662.     pcbPtr = proc_PCBTable[i];
  663.     if (pcbPtr->state == PROC_UNUSED) {
  664.         continue;
  665.     }
  666.     if ((*booleanFuncPtr)(pcbPtr)) {
  667.         pidArray[matched] = pcbPtr->processID;
  668.         matched++;
  669.     }
  670.     }
  671.     UNLOCK_MONITOR;
  672.     return(matched);
  673. }
  674.